package com.digitalinfinity.deep;

import android.os.Handler;
import android.content.Context;
import java.util.Random;
import static android.opengl.GLES10.*;
import static android.opengl.GLUtils.*;
import java.nio.*;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.*;
import android.graphics.*;
import net.rbgrn.android.glwallpaperservice.*;

public class DeepWallpaper extends GLWallpaperService {
	public class DeepRenderer implements GLWallpaperService.Renderer
	{
		int TTL=250;
		int SPAWNINTERVAL=5;
		float MAXROTVEL=0.72f;
    	int[] textures=new int[8];
	    Context mContext;
	    GLEngine gl_surface_view;
	    int spawn_i=0;
    	class FLARE
    	{
    		float[] pos;
    		float angle;
    		float rotvel;
    		float scale;
    		float initsize;
    		float finsize;
    		long ttl;
    		int t;
    	};
    	FLARE[] flares=new FLARE[(int)Math.ceil(TTL/SPAWNINTERVAL)];
    	int fnum=0;
    	Random random;
        final static int VERTS = 4;
        FloatBuffer mFVertexBuffer;
        FloatBuffer mTexBuffer;
    	float fw=1,fh=1;
    	Handler draw_handler=new Handler();
    	public float off_x=0,off_y=0;
    	
    	private final Runnable draw_run=new Runnable()
    	{
    		public void run()
    		{
    			gl_surface_view.requestRender();
    		}
    	};
    	
	    public DeepRenderer(Context context,GLEngine glsv) {
	        mContext = context;
	        gl_surface_view=glsv;
	    }

	    public void onSurfaceCreated(GL10 gl, EGLConfig config) 
	    {
	    	glDisable(GL_DEPTH_TEST);
	    	glDisable(GL_DITHER);
            glEnable(GL_TEXTURE_2D);
	        glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_FASTEST);
	        glClearColor(0,0,0,1);
	        glShadeModel(GL_SMOOTH);
	    	glEnable(GL_BLEND);
	    	glDisable(GL_FOG);
	    	glDisable(GL_LIGHTING);
	    	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
            glFrontFace(GL_CCW);
	    	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	    	glEnableClientState(GL_VERTEX_ARRAY);
	        glActiveTexture(GL_TEXTURE0);
            glGenTextures(8,textures,0);
            int[] bitmaps={
            		R.drawable.deep0,
            		R.drawable.deep1,
            		R.drawable.deep2,
            		R.drawable.deep3,
            		R.drawable.deep4,
            		R.drawable.deep5,
            		R.drawable.deep6,
            		R.drawable.deep7,
            };
            for(int i=0;i<8;i++)
            {
            	glBindTexture(GL_TEXTURE_2D,textures[i]);
                glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
                glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    	        glTexParameterx(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
    	        glTexParameterx(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
                glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
            	texImage2D(GL_TEXTURE_2D,0,BitmapFactory.decodeResource(mContext.getResources(),bitmaps[i]),0);
            }
            ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);
            vbb.order(ByteOrder.nativeOrder());
            mFVertexBuffer = vbb.asFloatBuffer();
            ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);
            tbb.order(ByteOrder.nativeOrder());
            mTexBuffer = tbb.asFloatBuffer();
			float[] coords = { 
					0.5f, -0.5f, 0, 
					0.5f, 0.5f, 0, 
					-0.5f, -0.5f, 0,
					-0.5f, 0.5f, 0, 
			};
            for (int i = 0; i < VERTS; i++)for(int j = 0; j < 3; j++)mFVertexBuffer.put(coords[i*3+j] * 2.0f);
            for (int i = 0; i < VERTS; i++)for(int j = 0; j < 2; j++)mTexBuffer.put(coords[i*3+j] + 0.5f);
            mFVertexBuffer.position(0);
            mTexBuffer.position(0);
            random=new Random();
	    }

	    public void onDrawFrame(GL10 gl) 
	    {
	    	int i;
	    	
			draw_handler.removeCallbacks(draw_run);
			draw_handler.postDelayed(draw_run, 40);
	        glClear(GL_COLOR_BUFFER_BIT);
	        glMatrixMode(GL_MODELVIEW);
	        glLoadIdentity();
            glVertexPointer(3, GL_FLOAT, 0, mFVertexBuffer);
            glTexCoordPointer(2, GL_FLOAT, 0, mTexBuffer);
            if(spawn_i==0) {
            	for(i=0;i<fnum;i++)if(flares[i]==null||flares[i].ttl==0)break;
            	if(i==fnum)fnum++;
            	if (flares[i] == null) 
            	{
            		flares[i] = new FLARE();
            		flares[i].pos = new float[2];
            	}
            	flares[i].ttl = TTL;
            	flares[i].angle = 0;
            	flares[i].initsize = 0.1f;
            	flares[i].finsize = (float) (random.nextFloat()) + 1;
            	flares[i].pos[0] = (float) (random.nextFloat())*fw*3-fw; // We can have some virtual screens with x offsets in [0,1] so we should extend flares generation to (-1,2) interval
            	flares[i].pos[1] = (float) (random.nextFloat())*fh*2-fh;
            	flares[i].rotvel = (float) (random.nextFloat()) * MAXROTVEL*2 - MAXROTVEL;
            	flares[i].t = textures[(int) Math.floor(random.nextFloat() * 8)];
            }
            spawn_i++;
            if(spawn_i==SPAWNINTERVAL)spawn_i=0;
			for (i = 0; i < fnum; i++) 
			{
				if (flares[i].ttl <= 0)continue;
				flares[i].angle += flares[i].rotvel;
				flares[i].scale = flares[i].initsize * flares[i].ttl / TTL + flares[i].finsize * (TTL - flares[i].ttl) / TTL;
				glLoadIdentity();
				glTranslatef(-off_x,-off_y,0);
				glTranslatef(flares[i].pos[0], flares[i].pos[1], 0);
				glRotatef(flares[i].angle, 0, 0, 1);
				glScalef(flares[i].scale, flares[i].scale, flares[i].scale);
				glColor4f(1, 1, 1, ((float) flares[i].ttl) / TTL);
				glBindTexture(GL_TEXTURE_2D, flares[i].t);
				glDrawArrays(GL_TRIANGLE_STRIP, 0, VERTS);
				flares[i].ttl--;
			}
	    }

	    public void onSurfaceChanged(GL10 gl, int w, int h) {
	        glViewport(0, 0, w, h);
	        if(w>h)
	        {
	        	fw=(float)w/h;
	        	fh=1;
	        }
	        else
	        {
	        	fw=1;
	        	fh=(float)h/w;
	        }
	        glMatrixMode(GL_PROJECTION);
	        glLoadIdentity();
	        glOrthof(-fw,fw,-fh,fh,-1,1);
	        glMatrixMode(GL_MODELVIEW);
	    }
	    
	    public void release() {
	    }
	}
	
	class DeepEngine extends GLEngine {
		DeepRenderer renderer;
		
		public DeepEngine(GLWallpaperService glws) {
			super();
			renderer=new DeepRenderer(glws,this);
			setRenderer(renderer);
			setRenderMode(RENDERMODE_WHEN_DIRTY);
		}
		
		public void onDestroy() {
			super.onDestroy();
			if(renderer!=null)renderer.release();
			renderer=null;
		}
		
		public void onOffsetsChanged(float xOffset,float yOffset,float xOffsetStep,float yOffsetStep,int xPixelOffset,int yPixelOffset) {
			if(renderer!=null) {
				renderer.off_x=xOffset;
				renderer.off_y=yOffset;
			}
		}
	}
	
	public DeepWallpaper() {
		super();
	}
	
	public Engine onCreateEngine() {
		return new DeepEngine(this);
	}
}
